#pragma once

#include <iostream>
#include <Windows.h>
#include <conio.h>
#include <vector>
#include <string>

#define KEY_ENTER 13
#define KEY_SPACEBAR 32	
#define KEY_ARROW_UP 72
#define KEY_ARROW_LEFT 75
#define KEY_ARROW_RIGHT 77	
#define KEY_ARROW_DOWN 80

#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif

const int DEFAULT_OPTIONS_PER_PAGE = 10;
const char	DEFAULT_CURSOR_STYLE = L'>';
const int TRUNC_CHARS = 5;
const std::wstring NO_INSTRUCTION = L"";

namespace NahidaProject {
	namespace ChooseDialog {
		class __declspec(dllexport) Option {
		public:
			BOOL IsSelected() const {
				return _selected;
			}
			
			Option& operator=(const NahidaProject::ChooseDialog::Option&) {
				Option opt;
				return opt;
			}

			const std::wstring _displayName;
			const std::wstring _description;
			friend class BasicMenu;
			~Option() {

			}

		private:
			BOOL _selected;

			Option(const std::wstring& optName, const std::wstring& optDescription) : _displayName(optName), _description(optDescription), _selected(FALSE) {

			};
			Option(const std::wstring& optName) : Option(optName, L"") {

			}
			Option() : Option(L"") {

			}

		protected:

		};


		struct __declspec(dllexport) MenuState {
			std::vector<Option> options;

			std::vector<Option> GetSelectedOptions() {
				std::vector<Option> selectedOpts;
				for (const auto& opt : options) {
					if (opt.IsSelected()) {
						selectedOpts.push_back(opt);
					}
				}
				return selectedOpts;
			}

			// d-tor
			~MenuState() {

			}

			friend class BasicMenu;

		private:
			MenuState() : options({}) {}
			MenuState(std::vector<Option>& options) : options(options) {

			}
		};

		class __declspec(dllexport) BasicMenu
		{
		public:

			virtual void AddOption(const std::wstring& optDisplayName, const std::wstring& optDescription = L"") {
				m_options.push_back({ optDisplayName, optDescription });
			}

			virtual void Execute() = 0;

			MenuState GetState() {
				return MenuState(m_options);
			}

		private:
		protected:
			BasicMenu(const std::wstring& menuTitle) : m_title(menuTitle), m_options({}) {}
			~BasicMenu() {};

			std::wstring m_title;
			std::vector<Option> m_options;
			int m_menuCursorPos = 0;

			virtual void RenderTitle() = 0;
			virtual void RenderOption(int optIdx) = 0;
			virtual void RenderDescription(int optIdx) = 0;
			virtual void DeleteDescription() = 0;
			virtual void ScrollConsole() = 0;

			std::wstring TruncateString(const std::wstring& str, int maxSize);

			void HideConsoleCursor();
			void ShowConsoleCursor();
			int GetConsoleLineSize();
			void ClearInputBuffer();
			void ClearLine();
			void ClearLeft(int chars);
			void ClearRight(int chars);
			void MoveConsoleCursorLeft(int chars);
			void MoveConsoleCursorRight(int chars);
			void MoveConsoleCursorUp(int lines);
			void MoveConsoleCursorDown(int lines);
			void ToggleOption(Option& option);
			void SelectOption(Option& option);
			void UnselectOption(Option& option);
		};


		class __declspec(dllexport) VerticalMenu : public BasicMenu
		{
		public:
			virtual void AddOption(const std::wstring& optDisplayName, const std::wstring& optDescription = L"") override {
				BasicMenu::AddOption(optDisplayName, optDescription);
				m_totalPages = (m_options.size() + m_OPTIONS_PER_PAGE - 1) / m_OPTIONS_PER_PAGE;
				m_B_USE_PAGING = m_totalPages > 1;
			}

		private:

			void RenderInstruction();
			void RenderPageInfo(int pageIdx);

		protected:

			VerticalMenu(const std::wstring& menuTitle, wchar_t cursorStyle, int optsPerPage, const std::wstring& instruction) : BasicMenu(menuTitle), m_cursorStyle(cursorStyle), m_OPTIONS_PER_PAGE(optsPerPage), m_instruction(instruction) {

			};


			VerticalMenu(const std::wstring& menuTitle, wchar_t cursorStyle, int optsPerPage) : VerticalMenu(menuTitle, cursorStyle, optsPerPage, NO_INSTRUCTION) {

			};

			VerticalMenu(const std::wstring& menuTitle, int optsPerPage, const std::wstring& instruction) : VerticalMenu(menuTitle, DEFAULT_CURSOR_STYLE, optsPerPage, instruction) {

			};

			VerticalMenu(const std::wstring& menuTitle, wchar_t cursorStyle, const std::wstring& instruction) : VerticalMenu(menuTitle, cursorStyle, DEFAULT_OPTIONS_PER_PAGE, instruction) {

			};


			VerticalMenu(const std::wstring& menuTitle, int optsPerPage) : VerticalMenu(menuTitle, DEFAULT_CURSOR_STYLE, optsPerPage, NO_INSTRUCTION) {

			};

			VerticalMenu(const std::wstring& menuTitle, wchar_t cursorStyle) : VerticalMenu(menuTitle, cursorStyle, DEFAULT_OPTIONS_PER_PAGE, NO_INSTRUCTION) {

			};

			VerticalMenu(const std::wstring& menuTitle, const std::wstring& instruction) : VerticalMenu(menuTitle, DEFAULT_CURSOR_STYLE, DEFAULT_OPTIONS_PER_PAGE, instruction) {

			};


			VerticalMenu(const std::wstring& menuTitle) : VerticalMenu(menuTitle, DEFAULT_CURSOR_STYLE, DEFAULT_OPTIONS_PER_PAGE, NO_INSTRUCTION) {

			};

			~VerticalMenu() {

			};

			std::wstring m_instruction;
			const wchar_t m_cursorStyle;
			int m_OPTIONS_PER_PAGE;
			BOOL m_B_USE_PAGING = FALSE;
			int m_currentPageIdx = 0;
			int m_totalPages = 0;

			void ScrollConsole() override;
			void RenderFooter();
			void RenderTitle() override;
			void RenderOption(int optIdx) override;
			BOOL IsOptionInPage(int optIdx, int pageIdx);
			int GetNumOptionsInPage(int pageIdx);
			void DeletePage();
			void RenderPage(int pageIdx);
			void HideMenuCursor();
			void DeleteDescription() override;
			void RenderDescription(int optIdx) override;
		};

		class __declspec(dllexport) CheckboxMenu : public VerticalMenu
		{
		public:
			CheckboxMenu(const std::wstring& menuTitle, wchar_t cursorStyle,
				int optsPerPage, const std::wstring& instruction)
				: VerticalMenu(menuTitle, cursorStyle, optsPerPage, instruction) {
			};


			CheckboxMenu(const std::wstring& menuTitle, wchar_t cursorStyle, int optsPerPage)
				: VerticalMenu(menuTitle, cursorStyle, optsPerPage) {
			};

			CheckboxMenu(const std::wstring& menuTitle, int optsPerPage,
				const std::wstring& instruction)
				: VerticalMenu(menuTitle, optsPerPage, instruction) {
			};

			CheckboxMenu(const std::wstring& menuTitle, wchar_t cursorStyle,
				const std::wstring& instruction)
				: VerticalMenu(menuTitle, cursorStyle, instruction) {
			};


			CheckboxMenu(const std::wstring& menuTitle, const std::wstring& instruction)
				: VerticalMenu(menuTitle, instruction) {
			};

			CheckboxMenu(const std::wstring& menuTitle, int optsPerPage)
				: VerticalMenu(menuTitle, optsPerPage) {
			};

			CheckboxMenu(const std::wstring& menuTitle, wchar_t cursorStyle)
				: VerticalMenu(menuTitle, cursorStyle) {
			};


			CheckboxMenu(const std::wstring& menuTitle)
				: VerticalMenu(menuTitle) {
			};


			void AddOption(const std::wstring& optDisplayName, const std::wstring& optDescription = L"", BOOL isSelectedByDefault = FALSE);
			void Execute() override;

		private:

		protected:

		};

		class __declspec(dllexport) RadioMenu : public VerticalMenu
		{
		public:
			RadioMenu(const std::wstring& menuTitle, wchar_t cursorStyle, int optsPerPage, const std::wstring& instruction) : VerticalMenu(menuTitle, cursorStyle, optsPerPage, instruction) {

			};

			RadioMenu(const std::wstring& menuTitle, wchar_t cursorStyle, int optsPerPage) : VerticalMenu(menuTitle, cursorStyle, optsPerPage) {

			};

			RadioMenu(const std::wstring& menuTitle, int optsPerPage, const std::wstring& instruction) : VerticalMenu(menuTitle, optsPerPage, instruction) {

			};

			RadioMenu(const std::wstring& menuTitle, wchar_t cursorStyle, const std::wstring& instruction) : VerticalMenu(menuTitle, cursorStyle, instruction) {

			};

			RadioMenu(const std::wstring& menuTitle, const std::wstring& instruction) : VerticalMenu(menuTitle, instruction) {

			};

			RadioMenu(const std::wstring& menuTitle, int optsPerPage) : VerticalMenu(menuTitle, optsPerPage) {

			};

			RadioMenu(const std::wstring& menuTitle, wchar_t cursorStyle) : VerticalMenu(menuTitle, cursorStyle) {

			};


			RadioMenu(const std::wstring& menuTitle) : VerticalMenu(menuTitle) {

			};

			void Execute() override;

		private:
			int m_selectedOptIdx = -1;

		protected:
		};

		class __declspec(dllexport) HorizontalMenu : public BasicMenu {
		public:

		private:

		protected:
			size_t m_maxOptLength = 8;

			HorizontalMenu(const std::wstring& menuTitle) : BasicMenu(menuTitle) {

			};
			~HorizontalMenu() {

			};

			void ScrollConsole() override;
			void RenderTitle() override;
			void RenderOption(int optIdx) override;
			void DeleteDescription() override;
			void RenderDescription(int optIdx) override;
				
		};

		class __declspec(dllexport) PromptMenu : public HorizontalMenu {
		public:
			PromptMenu(const std::wstring& menuTitle) : HorizontalMenu(menuTitle) {};

			void Execute() override;

		private:
		protected:
		};
	}
}
